﻿// <copyright file="TemplateScanner.partial.cs" company="Oleg Sych">
//  Copyright © Oleg Sych. All Rights Reserved.
// </copyright>

namespace T4Toolbox.TemplateAnalysis
{
    using System;
    using System.CodeDom.Compiler;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;

    /// <summary>
    /// LEX-based template scanner.
    /// </summary>
    [GeneratedCode("GPLEX", "1.5")] // To prevent FxCop from analyzing it
    internal partial class TemplateScanner
    {
        // Stores start of a token to enable handling of unclosed code blocks and attribute values
        private int tokenStart;
        private Position tokenPosition;
        private Position endOfFilePosition;

        public TemplateScanner(string input)
        {
            this.buffer = new TemplateBuffer(this, input);
            this.code = '\n'; // to initialize yyline, yycol and lineStart
            this.GetCode();
        }

        internal TemplateParser Parser { get; set; }

        private Position CurrentPosition
        {
            get { return new Position(this.tokLin - 1, Math.Max(0, this.tokCol)); } 
        }

        [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:ElementMustBeginWithUpperCaseLetter", Justification = "This method is defined by GPPG.")]
        public override void yyerror(string format, params object[] args)
        {
            Debug.Assert(this.Parser != null, "Parser is not initialized");
            this.Parser.Error(string.Format(CultureInfo.CurrentCulture, format, args));
        }

        private void BeginToken(int nextState)
        {
            this.BEGIN(nextState);
            this.tokenStart = this.tokEPos;
            this.tokenPosition = new Position(this.tokELin - 1, this.tokECol);
        }

        private void DiscardToken()
        {
            this.code = 0; // prevent reset of this.cCol in GetCode
            this.yyless(0);
        }

        /// <summary>
        /// Copy of the <see cref="StringBuffer"/> generated by GPLEX modified to handle EOF location correctly.
        /// </summary>
        private sealed class TemplateBuffer : ScanBuff
        {
            private readonly TemplateScanner scanner;
            private readonly string source;
            private int position;
            private int length;

            public TemplateBuffer(TemplateScanner scanner, string source)
            {
                Debug.Assert(scanner != null, "scanner");
                Debug.Assert(source != null, "source");

                this.scanner = scanner;
                this.source = source;
                this.length = source.Length;
                this.FileName = null;
            }

            public override int Pos
            {
                get { return this.position; }
                set { this.position = value; }
            }

            public override int Read()
            {
                if (this.position < this.length)
                {
                    return this.source[this.position++];
                }

                if (this.position == this.length)
                {
                    // Workaround for generated TemplateScanner loosing track of EOF position.
                    if (this.scanner.endOfFilePosition.Equals(default(Position)))
                    {
                        this.scanner.endOfFilePosition = new Position(this.scanner.lNum - 1, this.scanner.cCol + 1); 
                    }

                    this.position++;
                    return '\n'; 
                }

                this.position++;
                return ScanBuff.EndOfFile;
            }

            public override string GetString(int startPosition, int endPosition)
            {
                if (endPosition > this.length)
                {
                    endPosition = this.length;
                }

                if (endPosition <= startPosition)
                {
                    return string.Empty;
                }

                return this.source.Substring(startPosition, endPosition - startPosition);
            }
        }
    }
}